Java集合(8)--HashMap源码分析
/** * The default initial capacity - MUST be a power of two. */ static final int DEFAULT_INITIAL_CAPACITY = 16; /** * The maximum capacity, used if a higher value is implicitly specified * by either of the constructors with arguments. * MUST be a power of two <= 1<<30. */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * The load factor used when none specified in constructor. */ static final float DEFAULT_LOAD_FACTOR = 0.75f;
上面是一些变量的默认值
public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // Find a power of 2 >= initialCapacity //找到一个大于initialCapacity的数,并且这个数是2^n中大于initialCapacity的最小的一个数 int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; this.loadFactor = loadFactor; //计算阈值 threshold = (int)(capacity * loadFactor); table = new Entry[capacity]; init(); }
获得hashcode的索引,length是散列表的长度
static int indexFor(int h, int length) { return h & (length-1); }
get方法:
public V get(Object key) { if (key == null) return getForNullKey(); //计算hashcode的hash码 int hash = hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; //既要hashcode相同,又要key相等,调用equals方法 //key在方法开头的if语句就判断了,这里不必担心key为null if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
put方法:
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value;
e.recordAccess(this); //返回的是旧的值 return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
resize方法:
void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; //到最大容量了,不能再增加了 if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); }
transfer方法:重新拷贝值到新数组,重新计算hash,工作量大
void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j]; if (e != null) { src[j] = null; do { Entry<K,V> next = e.next; //重新计算hash int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } }
当要加入的键值对个数大于阈值时,重新计算目标空间targetCapacity=(numKeysToBeAdded / loadFactor) + 1,还要找到一个新容量newCapacity大于targetCapacity,且是2^n的最小值,再resize
public void putAll(Map<? extends K, ? extends V> m) { int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; if (numKeysToBeAdded > threshold) { int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); if (targetCapacity > MAXIMUM_CAPACITY) targetCapacity = MAXIMUM_CAPACITY; int newCapacity = table.length; while (newCapacity < targetCapacity) newCapacity <<= 1; if (newCapacity > table.length) resize(newCapacity); } for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); }
删除键:
final Entry<K,V> removeEntryForKey(Object key) { //计算hash码 int hash = (key == null) ? 0 : hash(key.hashCode()); int i = indexFor(hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> e = prev; while (e != null) { Entry<K,V> next = e.next; Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } //也就是链表的删除 prev = e; e = next; } return e; }
addEntry方法:
void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); // 容量要大于阈值时,重设大小为原来的两倍 if (size++ >= threshold) resize(2 * table.length); }